<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
  <title>JSON树结构转换工具</title>
  <!-- 引入Tailwind CSS -->
  <script src="https://cdn.tailwindcss.com"></script>
  <!-- 引入Font Awesome -->
  <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
  
  <!-- Tailwind配置 -->
  <script>
    tailwind.config = {
      theme: {
        extend: {
          colors: {
            primary: '#3B82F6',
            secondary: '#10B981',
            dark: '#1E293B',
            light: '#F8FAFC',
            danger: '#EF4444',
            warning: '#F59E0B',
            editor: '#0F172A',
            tree: '#F1F5F9'
          },
          fontFamily: {
            mono: ['Fira Code', 'Consolas', 'Monaco', 'monospace'],
            sans: ['Inter', 'system-ui', 'sans-serif']
          },
        },
      }
    }
  </script>
  
  <!-- 自定义工具类 -->
  <style type="text/tailwindcss">
    @layer utilities {
      .content-auto {
        content-visibility: auto;
      }
      .editor-line {
        @apply border-l-2 border-transparent pl-3 py-0.5 hover:border-primary/30 transition-colors;
      }
      .editor-line-error {
        @apply border-l-2 border-danger bg-danger/10;
      }
      .error-highlight {
        @apply bg-danger/20 px-1 rounded;
      }
      .tree-node-active {
        @apply bg-primary/10 rounded-md;
      }
      .shadow-depth {
        box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
      }
      .text-balance {
        text-wrap: balance;
      }
      .resize-handle {
        @apply bg-slate-300 hover:bg-slate-400 transition-colors cursor-col-resize;
      }
      .resize-handle-vertical {
        @apply bg-slate-300 hover:bg-slate-400 transition-colors cursor-row-resize;
      }
    }
  </style>
  
  <style>
    /* 基础样式重置 */
    * {
      box-sizing: border-box;
    }
    
    html, body {
      height: 100%;
      overflow: hidden;
    }
    
    /* 自定义滚动条 */
    ::-webkit-scrollbar {
      width: 8px;
      height: 8px;
    }
    ::-webkit-scrollbar-track {
      background: transparent;
    }
    ::-webkit-scrollbar-thumb {
      background: #CBD5E1;
      border-radius: 4px;
    }
    ::-webkit-scrollbar-thumb:hover {
      background: #94A3B8;
    }
    
    /* 动画效果 */
    .fade-in {
      animation: fadeIn 0.3s ease-in-out;
    }
    @keyframes fadeIn {
      from { opacity: 0; }
      to { opacity: 1; }
    }
    
    .slide-up {
      animation: slideUp 0.3s ease-out;
    }
    @keyframes slideUp {
      from { transform: translateY(20px); opacity: 0; }
      to { transform: translateY(0); opacity: 1; }
    }
    
    .slide-down {
      animation: slideDown 0.2s ease-out;
    }
    @keyframes slideDown {
      from { transform: translateY(-10px); opacity: 0; }
      to { transform: translateY(0); opacity: 1; }
    }
    
    .pulse {
      animation: pulse 1.5s infinite;
    }
    @keyframes pulse {
      0% { opacity: 1; }
      50% { opacity: 0.7; }
      100% { opacity: 1; }
    }
    
    .node-updating {
      background-color: rgba(59, 130, 246, 0.1);
      transition: background-color 0.3s ease;
    }
    
    /* 错误提示相关样式 */
    .error-tooltip {
      position: relative;
    }
    
    .error-tooltip:hover::after {
      content: attr(data-error);
      position: absolute;
      bottom: 100%;
      left: 0;
      background-color: #1E293B;
      color: white;
      padding: 4px 8px;
      border-radius: 4px;
      font-size: 12px;
      max-width: 300px;
      white-space: normal;
      z-index: 100;
      box-shadow: 0 2px 5px rgba(0,0,0,0.2);
    }
    
    /* 错误上下文预览 */
    .error-context {
      background-color: #FEF3F2;
      border-left: 3px solid #EF4444;
      padding: 8px;
      margin: 8px 0;
      font-family: monospace;
      font-size: 13px;
      border-radius: 0 4px 4px 0;
      overflow-x: auto;
    }
    
    .error-context-line {
      position: relative;
      padding-left: 30px;
      white-space: pre-wrap;
      line-height: 1.5;
    }
    
    .error-context-line::before {
      content: attr(data-line);
      position: absolute;
      left: 0;
      width: 24px;
      text-align: right;
      color: #9CA3AF;
      padding-right: 6px;
      user-select: none;
    }
    
    .error-context-highlight {
      background-color: rgba(239, 68, 68, 0.2);
      padding: 0 2px;
      border-radius: 2px;
      text-decoration: underline wavy #EF4444;
    }
    /* 响应式布局调整 */
    @media (max-width: 1023px) {
      .tool-container {
        flex-direction: column;
        height: 100vh;
      }
      
      .panel {
        width: 100% !important;
        height: 50% !important;
        max-height: none !important;
      }
      
      .resize-handle {
        display: none !important;
      }
      
      .resize-handle-vertical {
        display: block !important;
        height: 6px !important;
        width: 100% !important;
      }
      
      .header-actions {
        flex-wrap: wrap;
        gap: 0.5rem;
      }
      
      .header-actions button {
        padding: 0.375rem 0.75rem;
        font-size: 0.875rem;
      }
    }
    
    @media (max-width: 639px) {
      .header-title h1 {
        font-size: 1.25rem;
      }
      
      .header-actions {
        justify-content: center;
        width: 100%;
        margin-top: 0.75rem;
      }
      
      .header-actions button span {
        display: none;
      }
      
      .header-actions button i {
        margin-right: 0;
      }
      
      footer {
        font-size: 0.75rem;
      }
      
      .footer-info:last-child {
        display: none;
      }
    }
  </style>
</head>

<body class="bg-slate-50 text-slate-800 h-screen flex flex-col">
  <!-- 顶部导航栏 -->
  <header class="bg-white shadow-md z-10 transition-all duration-300">
    <div class="container mx-auto px-4 py-3 flex flex-wrap items-center justify-between">
      <div class="flex items-center space-x-2 header-title">
        <i class="fa fa-code text-primary text-2xl"></i>
        <h1 class="text-xl md:text-2xl font-bold bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent">
          JSON树结构转换工具
        </h1>
      </div>
      
      <div class="flex items-center space-x-3 mt-2 sm:mt-0 header-actions">
        <!-- 按钮已移动到树状结构面板 -->
      </div>
    </div>
  </header>
  
  <!-- 主内容区 - 带可调整大小功能 -->
  <div class="flex-1 flex tool-container overflow-hidden">
    <!-- 左侧JSON编辑器面板 -->
    <div class="panel w-1/2 flex flex-col h-full max-h-full">
      <div class="bg-white rounded-t-lg px-4 py-2 border-b border-slate-200 flex justify-between items-center">
        <h2 class="font-semibold flex items-center">
          <i class="fa fa-file-code-o text-primary mr-2"></i>
          JSON文本
        </h2>
        <div class="flex items-center space-x-2 ml-4">
              <button id="format-btn" class="text-sm flex items-center space-x-1 px-2 py-1 bg-primary text-white rounded-md hover:bg-primary/90 transition-all shadow-sm hover:shadow">
                <i class="fa fa-indent"></i>
                <span>格式化</span>
              </button>
              <button id="compress-btn" class="text-sm flex items-center space-x-1 px-2 py-1 bg-slate-700 text-white rounded-md hover:bg-slate-800 transition-all shadow-sm hover:shadow">
                <i class="fa fa-compress"></i>
                <span>压缩</span>
              </button>
              <button id="copy-btn" class="text-sm flex items-center space-x-1 px-2 py-1 bg-secondary text-white rounded-md hover:bg-secondary/90 transition-all shadow-sm hover:shadow">
                <i class="fa fa-copy"></i>
                <span>复制</span>
              </button>
              <button id="clear-btn" class="text-sm flex items-center space-x-1 px-2 py-1 bg-danger text-white rounded-md hover:bg-danger/90 transition-all shadow-sm hover:shadow">
                <i class="fa fa-trash"></i>
                <span>清空</span>
              </button>
            </div>
        <div id="json-status" class="text-sm hidden items-center">
          <span id="json-valid" class="text-secondary hidden items-center">
            <i class="fa fa-check-circle mr-1"></i>有效
          </span>
          <span id="json-invalid" class="text-danger hidden items-center">
            <i class="fa fa-exclamation-circle mr-1"></i>格式错误
          </span>
        </div>
      </div>
      
      <div class="relative flex-1 bg-editor rounded-b-lg overflow-hidden shadow-depth flex flex-col">
        <pre id="line-numbers" class="absolute top-0 left-0 h-full w-10 flex flex-col items-end justify-start text-slate-500 text-xs py-2 pr-2 select-none pointer-events-none"></pre>
        <div id="editor-wrapper" class="relative w-full flex-1 overflow-hidden">
          <textarea 
            id="json-editor" 
            class="w-full h-full bg-transparent text-slate-200 font-mono text-sm resize-none border-none outline-none p-2 pl-12 pr-4 leading-relaxed"
            placeholder="请输入JSON数据..."></textarea>
          <!-- 错误标记覆盖层 -->
          <div id="error-overlay" class="absolute top-0 left-0 w-full h-full pointer-events-none overflow-hidden"></div>
        </div>
      </div>
    </div>
    
    <!-- 水平调整大小手柄 - 仅桌面可见 -->
    <div class="resize-handle w-1 h-full hidden md:block" id="horizontal-resizer"></div>
    
    <!-- 垂直调整大小手柄 - 仅移动设备可见 -->
    <div class="resize-handle-vertical h-0 w-full md:hidden" id="vertical-resizer"></div>
    
    <!-- 右侧树结构面板 -->
    <div class="panel w-1/2 flex flex-col h-full max-h-full">
      <div class="bg-white rounded-t-lg px-4 py-2 border-b border-slate-200 flex justify-between items-center">
        <h2 class="font-semibold flex items-center">
          <i class="fa fa-sitemap text-secondary mr-2"></i>
          树状结构
        </h2>
        <div class="flex items-center space-x-2">
          <!-- 展开/折叠切换按钮 -->
          <button id="toggle-expand-collapse" class="text-sm text-slate-600 hover:text-primary transition-colors flex items-center">
            <i class="fa fa-plus-square-o mr-1"></i>
            <span>全部展开</span>
          </button>
        </div>
      </div>
      
      <div id="tree-view" class="flex-1 bg-white rounded-b-lg overflow-hidden shadow-depth relative flex flex-col">
        <!-- 树状结构区域 -->
        <div class="flex-1 overflow-auto p-4">
          <div id="tree-container" class="space-y-1 font-mono text-sm"></div>
          <div id="empty-state" class="h-full flex flex-col items-center justify-center text-slate-400">
            <i class="fa fa-file-text-o text-5xl mb-4 opacity-30"></i>
            <p class="text-center max-w-md text-balance">输入JSON数据后，这里将显示对应的树状结构</p>
            <p class="mt-2 text-sm">支持实时编辑和双向同步，严格保持节点原有顺序</p>
          </div>
        </div>
        
        <!-- 底部错误提示面板 (默认显示) -->
        <div id="error-panel" class="absolute bottom-0 left-0 right-0 bg-white border-t border-danger/30 shadow-lg p-3 max-h-[60%] overflow-auto transform translate-y-full opacity-0 transition-all duration-300">
          <div class="flex items-start">
            <div class="text-danger mr-3 mt-0.5">
              <i class="fa fa-exclamation-triangle text-xl pulse"></i>
            </div>
            <div class="flex-1">
              <div id="error-details" class="font-medium text-danger mb-1"></div>
              <div id="error-context" class="error-context text-sm text-slate-700 text-sm"></div>
              <div class="mt-2 text-xs text-slate-500">
                <i class="fa fa-lightbulb-o mr-1"></i>
                <span>提示: 请修正JSON格式错误后才能正确显示树状结构</span>
              </div>
            </div>
            <button id="close-error" class="text-slate-400 hover:text-slate-600 ml-2">
              <i class="fa fa-times"></i>
            </button>
          </div>
        </div>
      </div>
    </div>
  </div>
  
  <!-- 底部状态栏 -->
  <footer class="bg-white border-t border-slate-200 py-2 px-4">
    <div class="container mx-auto flex flex-wrap justify-between items-center text-sm text-slate-500">
      <div class="footer-info">
        <span id="node-count">0 个节点</span>
        <span class="mx-2">•</span>
        <span id="char-count">0 个字符</span>
        <span id="error-status" class="mx-2 hidden">•</span>
        <span id="error-location" class="text-danger hidden"></span>
      </div>
      <div class="footer-info">
        <span>提示: 编辑任意一侧将自动同步，保持节点原有顺序</span>
      </div>
    </div>
  </footer>
  
  <!-- 通知组件 -->
  <div id="notification" class="fixed bottom-4 right-4 px-4 py-3 rounded-md shadow-lg transform translate-y-20 opacity-0 transition-all duration-300 flex items-center space-x-2 z-50"></div>

  <script>
    // 全局状态
    const state = {
      jsonData: null,
      prevJsonData: null,
      editing: false,
      nodeCount: 0,
      charCount: 0,
      nodeStates: new Map(),
      lastError: null, // 存储最后一次的错误信息
      errorVisible: false, // 错误面板是否可见
      allExpanded: false, // 所有节点是否处于展开状态
      isResizing: false, // 是否正在调整大小
      panelRatio: 0.5, // 左右面板比例
      verticalPanelRatio: 0.5 // 移动端上下面板比例
    };
    
    // DOM元素
    const elements = {
      jsonEditor: document.getElementById('json-editor'),
      editorWrapper: document.getElementById('editor-wrapper'),
      errorOverlay: document.getElementById('error-overlay'),
      errorPanel: document.getElementById('error-panel'),
      errorDetails: document.getElementById('error-details'),
      errorContext: document.getElementById('error-context'),
      closeErrorBtn: document.getElementById('close-error'),
      toggleExpandCollapseBtn: document.getElementById('toggle-expand-collapse'),
      treeContainer: document.getElementById('tree-container'),
      treeView: document.getElementById('tree-view'),
      emptyState: document.getElementById('empty-state'),
      lineNumbers: document.getElementById('line-numbers'),
      jsonStatus: document.getElementById('json-status'),
      jsonValid: document.getElementById('json-valid'),
      jsonInvalid: document.getElementById('json-invalid'),
      errorStatus: document.getElementById('error-status'),
      errorLocation: document.getElementById('error-location'),
      nodeCount: document.getElementById('node-count'),
      charCount: document.getElementById('char-count'),
      notification: document.getElementById('notification'),
      formatBtn: document.getElementById('format-btn'),
      compressBtn: document.getElementById('compress-btn'),
      copyBtn: document.getElementById('copy-btn'),
      clearBtn: document.getElementById('clear-btn'),
      horizontalResizer: document.getElementById('horizontal-resizer'),
      verticalResizer: document.getElementById('vertical-resizer'),
      panels: document.querySelectorAll('.panel'),
      toolContainer: document.querySelector('.tool-container')
    };
    
    // 初始化示例数据
    function initSampleData() {
      try {
        const sampleData = {
          "name": "JSON树结构转换工具",
          "version": "1.0.0",
          "features": [
            "实时编辑",
            "双向同步",
            "严格保持顺序",
            "精确错误定位",
            "格式化",
            "压缩"
          ],
          "settings": {
            "theme": "light",
            "autoFormat": true,
            "indentSize": 2
          },
          "stats": {
            "users": 1500,
            "rating": 4.8,
            "isActive": true
          }
        };
        
        elements.jsonEditor.value = JSON.stringify(sampleData, null, 2);
        updateFromJsonEditor();
        updateLineNumbers();
      } catch (error) {
        console.error("初始化示例数据失败:", error);
        showNotification("初始化失败，请手动输入JSON数据", "error");
      }
    }
    
    // 更新行号
    function updateLineNumbers() {
      try {
        clearErrorMarkers(); // 清除错误标记
        
        const lines = elements.jsonEditor.value.split('\n').length;
        elements.lineNumbers.innerHTML = '';
        
        for (let i = 1; i <= lines; i++) {
          const lineNum = document.createElement('div');
          lineNum.textContent = i;
          lineNum.className = 'editor-line';
          lineNum.dataset.line = i;
          elements.lineNumbers.appendChild(lineNum);
        }
        
        // 如果有错误，重新标记错误行
        if (state.lastError && state.lastError.line) {
          markErrorLine(state.lastError.line);
        }
      } catch (error) {
        console.error("更新行号失败:", error);
      }
    }
    
    // 从JSON编辑器更新树结构
    function updateFromJsonEditor() {
      if (state.editing) return;
      
      try {
        state.editing = true;
        clearErrorMarkers(); // 清除之前的错误标记
        hideErrorPanel(); // 先隐藏，根据情况再显示
        state.lastError = null;
        elements.errorStatus.classList.add('hidden');
        elements.errorLocation.classList.add('hidden');
        
        const jsonText = elements.jsonEditor.value.trim();
        state.charCount = jsonText.length;
        elements.charCount.textContent = `${state.charCount} 个字符`;
        
        if (!jsonText) {
          state.jsonData = null;
          state.prevJsonData = null;
          elements.treeContainer.innerHTML = '';
          elements.emptyState.classList.remove('hidden');
          elements.jsonStatus.classList.add('hidden');
          state.nodeCount = 0;
          elements.nodeCount.textContent = `${state.nodeCount} 个节点`;
          state.nodeStates.clear();
          state.allExpanded = false;
          updateExpandCollapseButtonText();
          return;
        }
        
        // 解析JSON并处理错误
        let parsedData;
        try {
          parsedData = JSON.parse(jsonText);
        } catch (parseError) {
          // 解析错误信息并提取位置
          const errorInfo = parseJsonError(parseError, jsonText);
          state.lastError = errorInfo;
          
          // 标记错误位置
          markErrorLocation(errorInfo);
          
          // 准备并显示错误信息（默认显示）
          prepareErrorPanel(errorInfo, jsonText);
          showErrorPanel();
          
          // 更新状态显示
          elements.jsonStatus.classList.remove('hidden');
          elements.jsonValid.classList.add('hidden');
          elements.jsonInvalid.classList.remove('hidden');
          elements.emptyState.classList.remove('hidden');
          state.nodeCount = 0;
          elements.nodeCount.textContent = `${state.nodeCount} 个节点`;
          
          // 在状态栏显示错误位置
          elements.errorStatus.classList.remove('hidden');
          elements.errorLocation.classList.remove('hidden');
          elements.errorLocation.textContent = `错误: 第 ${errorInfo.line} 行，第 ${errorInfo.column} 列`;
          
          throw errorInfo; // 继续抛出错误以终止正常流程
        }
        
        const oldData = state.jsonData;
        state.prevJsonData = oldData ? JSON.parse(JSON.stringify(oldData)) : null;
        state.jsonData = parsedData;
        
        // 更新状态
        elements.jsonStatus.classList.remove('hidden');
        elements.jsonValid.classList.remove('hidden');
        elements.jsonInvalid.classList.add('hidden');
        
        // 计算节点数量
        state.nodeCount = countNodes(parsedData);
        elements.nodeCount.textContent = `${state.nodeCount} 个节点`;
        
        elements.emptyState.classList.add('hidden');
        
        // 首次加载或结构变化大时全量渲染
        if (!oldData || isStructureChanged(oldData, parsedData)) {
          elements.treeContainer.innerHTML = '';
          state.nodeStates.clear();
          state.allExpanded = false; // 重置展开状态
          renderTreeNode(parsedData, elements.treeContainer, 'root', true);
          updateExpandCollapseButtonText();
        } else {
          // 局部更新
          try {
            updateNodesInOrder(oldData, parsedData);
          } catch (diffError) {
            console.warn("顺序更新失败，将重新渲染整个树:", diffError);
            elements.treeContainer.innerHTML = '';
            state.nodeStates.clear();
            state.allExpanded = false; // 重置展开状态
            renderTreeNode(parsedData, elements.treeContainer, 'root', true);
            updateExpandCollapseButtonText();
          }
        }
        
      } catch (error) {
        if (!(error instanceof Error) && error.line) {
          // 已经处理过的JSON解析错误
        } else {
          // 其他错误
          console.error("处理JSON数据失败:", error);
          showNotification(`错误: ${error.message}`, 'error');
          elements.jsonStatus.classList.remove('hidden');
          elements.jsonValid.classList.add('hidden');
          elements.jsonInvalid.classList.remove('hidden');
          elements.emptyState.classList.remove('hidden');
        }
      } finally {
        state.editing = false;
      }
    }
    
    // 准备错误面板内容
    function prepareErrorPanel(errorInfo, jsonText) {
      elements.errorDetails.textContent = `JSON格式错误: ${errorInfo.message}`;
      showErrorContext(errorInfo, jsonText);
    }
    
    // 显示底部错误面板（默认显示）
    function showErrorPanel() {
      if (!state.lastError) return;
      
      elements.errorPanel.classList.remove('translate-y-full', 'opacity-0');
      elements.errorPanel.classList.add('slide-up');
      state.errorVisible = true;
    }
    
    // 隐藏底部错误面板
    function hideErrorPanel() {
      elements.errorPanel.classList.add('translate-y-full', 'opacity-0');
      elements.errorPanel.classList.remove('slide-up');
      state.errorVisible = false;
    }
    
    // 切换错误面板显示状态
    function toggleErrorPanel() {
      if (state.errorVisible) {
        hideErrorPanel();
      } else {
        showErrorPanel();
      }
    }
    
    // 解析JSON错误信息，提取错误位置和详细信息
    function parseJsonError(parseError, jsonText) {
      // 从错误消息中提取行号和列号
      let line = 0;
      let column = 0;
      let position = -1;
      let message = parseError.message || "JSON格式错误";
      
      // 现代浏览器通常会提供lineNumber和columnNumber属性
      if (parseError.lineNumber !== undefined && parseError.columnNumber !== undefined) {
        line = parseError.lineNumber;
        column = parseError.columnNumber;
      } 
      // 尝试从错误消息中提取位置信息
      else {
        // 匹配 "at position X" 格式
        const posMatch = message.match(/at position (\d+)/);
        if (posMatch && posMatch[1]) {
          position = parseInt(posMatch[1]);
        }
        // 匹配 "line X, column Y" 格式
        else {
          const lineColMatch = message.match(/line (\d+), column (\d+)/);
          if (lineColMatch && lineColMatch.length === 3) {
            line = parseInt(lineColMatch[1]);
            column = parseInt(lineColMatch[2]);
          }
        }
      }
      
      // 如果无法直接获取行号和列号，则通过位置计算
      if (line === 0 && position !== -1 && position <= jsonText.length) {
        // 计算到错误位置为止的换行符数量来确定行号
        const substring = jsonText.substring(0, position);
        line = (substring.match(/\n/g) || []).length + 1;
        
        // 计算当前行内的位置（列号）
        const lastNewline = substring.lastIndexOf('\n');
        column = (lastNewline === -1 ? position : position - lastNewline);
      }
      
      // 处理特殊情况和边界值
      const totalLines = (jsonText.match(/\n/g) || []).length + 1;
      line = Math.max(1, Math.min(line, totalLines));
      
      // 获取当前行文本以计算有效列号
      const lines = jsonText.split('\n');
      const lineText = lines[line - 1] || '';
      column = Math.max(1, Math.min(column, lineText.length + 1));
      
      // 增强错误消息，使其更易懂
      message = enhanceErrorMessage(message);
      
      return {
        message,
        line,
        column,
        position,
        originalError: parseError
      };
    }
    
    // 增强错误消息，使其更友好和易懂
    function enhanceErrorMessage(originalMessage) {
      // 常见JSON错误的更友好解释
      const errorMap = {
        "Unexpected token": "意外的符号，可能是缺少逗号或括号不匹配",
        "Unexpected end of JSON input": "JSON输入意外结束，可能缺少闭合括号、引号或数组括号",
        "Unexpected string": "意外的字符串，可能缺少逗号分隔",
        "Unexpected number": "意外的数字格式，检查是否有多余的小数点或非法字符",
        "Unexpected boolean": "意外的布尔值，检查是否拼写错误或缺少分隔符",
        "Unexpected null": "意外的null值，检查是否缺少分隔符",
        "Missing } in object": "对象缺少闭合的 }",
        "Missing ] in array": "数组缺少闭合的 ]",
        "Unterminated string": "未闭合的字符串，检查引号是否匹配或有转义错误",
        "Invalid or unexpected token": "无效或意外的符号，可能是使用了中文标点或特殊字符"
      };
      
      // 尝试匹配错误消息并替换为更友好的版本
      for (const [key, value] of Object.entries(errorMap)) {
        if (originalMessage.includes(key)) {
          return `${value} (${key})`;
        }
      }
      
      // 如果没有匹配到已知错误，返回原始消息
      return originalMessage;
    }
    
    // 显示错误上下文，帮助用户理解错误位置
    function showErrorContext(errorInfo, jsonText) {
      try {
        const lines = jsonText.split('\n');
        const lineNum = errorInfo.line;
        const colNum = errorInfo.column;
        
        // 获取错误行及其前后各一行作为上下文
        const startLine = Math.max(1, lineNum - 1);
        const endLine = Math.min(lines.length, lineNum + 1);
        
        let contextHtml = '';
        
        for (let i = startLine; i <= endLine; i++) {
          let lineText = lines[i - 1] || '';
          let lineHtml = lineText;
          
          // 对错误行进行特殊处理，高亮错误位置
          if (i === lineNum) {
            // 限制列号在有效范围内
            const highlightCol = Math.min(colNum - 1, lineText.length);
            
            // 分割文本以高亮错误位置
            const before = lineText.substring(0, highlightCol);
            const errorChar = lineText.substring(highlightCol, highlightCol + 1) || ' ';
            const after = lineText.substring(highlightCol + 1);
            
            lineHtml = `${escapeHtml(before)}<span class="error-context-highlight">${escapeHtml(errorChar)}</span>${escapeHtml(after)}`;
          } else {
            lineHtml = escapeHtml(lineText);
          }
          
          contextHtml += `<div class="error-context-line" data-line="${i}">${lineHtml}</div>`;
        }
        
        elements.errorContext.innerHTML = contextHtml;
      } catch (error) {
        console.error("显示错误上下文失败:", error);
        elements.errorContext.innerHTML = '<div>无法显示错误上下文</div>';
      }
    }
    
    // HTML转义函数
    function escapeHtml(unsafe) {
      if (!unsafe) return '';
      return unsafe
        .replace(/&/g, "&amp;")
        .replace(/</g, "&lt;")
        .replace(/>/g, "&gt;")
        .replace(/"/g, "&quot;")
        .replace(/'/g, "&#039;");
    }
    
    // 标记错误行
    function markErrorLine(lineNumber) {
      const lineElement = elements.lineNumbers.querySelector(`.editor-line[data-line="${lineNumber}"]`);
      if (lineElement) {
        lineElement.classList.add('editor-line-error');
      }
      
      // 滚动到错误行，使其居中显示
      const lineHeight = parseInt(getComputedStyle(elements.jsonEditor).lineHeight);
      const editorHeight = elements.jsonEditor.clientHeight;
      elements.jsonEditor.scrollTop = Math.max(0, (lineNumber - 1) * lineHeight - editorHeight / 2);
    }
    
    // 标记错误位置
    function markErrorLocation(errorInfo) {
      // 清除之前的错误标记
      clearErrorMarkers();
      
      // 标记错误行
      markErrorLine(errorInfo.line);
      
      // 在编辑器上覆盖显示错误位置标记
      const text = elements.jsonEditor.value;
      const lines = text.split('\n');
      
      if (errorInfo.line <= 0 || errorInfo.line > lines.length) {
        return;
      }
      
      const lineText = lines[errorInfo.line - 1] || '';
      const column = Math.min(errorInfo.column - 1, lineText.length);
      
      // 创建错误标记
      const marker = document.createElement('div');
      marker.className = 'absolute text-danger error-tooltip';
      marker.innerHTML = '<i class="fa fa-exclamation-circle"></i>';
      marker.dataset.error = errorInfo.message;
      
      // 获取编辑器字体信息以精确计算位置
      const style = getComputedStyle(elements.jsonEditor);
      const fontSize = parseFloat(style.fontSize);
      const lineHeight = parseFloat(style.lineHeight);
      const fontFamily = style.fontFamily;
      
      // 对于等宽字体，使用更精确的字符宽度计算
      let charWidth = fontSize * 0.5; // 默认近似值
      if (fontFamily.includes('monospace') || fontFamily.includes('Consolas') || fontFamily.includes('Fira Code')) {
        charWidth = fontSize * 0.58; // 等宽字体更精确的近似值
      }
      
      // 计算行号区域宽度（固定为30px）
      const lineNumbersWidth = 30;
      
      // 定位标记（考虑滚动偏移）
      marker.style.top = `${(errorInfo.line - 1) * lineHeight + 2}px`;
      marker.style.left = `${lineNumbersWidth + column * charWidth}px`;
      
      elements.errorOverlay.appendChild(marker);
    }
    
    // 清除所有错误标记
    function clearErrorMarkers() {
      // 清除行号错误标记
      document.querySelectorAll('.editor-line-error').forEach(el => {
        el.classList.remove('editor-line-error');
      });
      
      // 清除错误覆盖层
      elements.errorOverlay.innerHTML = '';
    }
    
    // 严格按顺序更新节点
    function updateNodesInOrder(oldData, newData, path = []) {
      const type = getType(newData);
      
      // 类型变化时重新渲染
      if (getType(oldData) !== type) {
        const nodeElement = findNodeByPath(elements.treeContainer, path);
        if (nodeElement && nodeElement.parentNode) {
          const parentNode = nodeElement.parentNode;
          const nextSibling = nodeElement.nextSibling;
          nodeElement.remove();
          
          // 相同位置插入新节点
          const tempContainer = document.createElement('div');
          renderTreeNode(newData, tempContainer, path.length > 0 ? path[path.length - 1] : 'root', path.length === 0);
          const newNode = tempContainer.firstElementChild;
          
          if (nextSibling) {
            parentNode.insertBefore(newNode, nextSibling);
          } else {
            parentNode.appendChild(newNode);
          }
        }
        return;
      }
      
      // 基本类型直接更新值
      if (!['object', 'array'].includes(type)) {
        if (oldData !== newData) {
          const nodeElement = findNodeByPath(elements.treeContainer, path);
          if (nodeElement) {
            const valueSpan = nodeElement.querySelector('.node-value');
            const valueInput = nodeElement.querySelector('.node-input');
            if (valueSpan && valueInput) {
              valueSpan.textContent = formatValue(newData, type);
              valueInput.value = formatValue(newData, type, true);
            }
          }
        }
        return;
      }
      
      // 处理null值
      if (newData === null) {
        if (oldData !== null) {
          const nodeElement = findNodeByPath(elements.treeContainer, path);
          if (nodeElement && nodeElement.parentNode) {
            const parentNode = nodeElement.parentNode;
            const nextSibling = nodeElement.nextSibling;
            nodeElement.remove();
            
            // 相同位置插入新节点
            const tempContainer = document.createElement('div');
            renderTreeNode(newData, tempContainer, path.length > 0 ? path[path.length - 1] : 'root', path.length === 0);
            const newNode = tempContainer.firstElementChild;
            
            if (nextSibling) {
              parentNode.insertBefore(newNode, nextSibling);
            } else {
              parentNode.appendChild(newNode);
            }
          }
        }
        return;
      }
      
      // 数组处理
      if (Array.isArray(newData)) {
        const nodeElement = findNodeByPath(elements.treeContainer, path);
        if (!nodeElement) return;
        
        const childrenContainer = nodeElement.querySelector('.node-children');
        if (!childrenContainer) return;
        
        // 保存展开状态
        const isExpanded = !childrenContainer.classList.contains('hidden');
        const key = path.join('.');
        state.nodeStates.set(key, isExpanded);
        
        // 先删除超出新长度的元素
        for (let i = oldData.length - 1; i >= 0; i--) {
          if (i >= newData.length) {
            removeNodeByPath(elements.treeContainer, [...path, i]);
          }
        }
        
        // 按索引顺序更新或添加
        for (let i = 0; i < newData.length; i++) {
          const childPath = [...path, i];
          if (i < oldData.length) {
            updateNodesInOrder(oldData[i], newData[i], childPath);
          } else {
            const parentNode = findNodeByPath(elements.treeContainer, path);
            if (parentNode) {
              const container = parentNode.querySelector('.node-children') || parentNode;
              
              // 创建新节点并添加到末尾
              const tempContainer = document.createElement('div');
              renderTreeNode(newData[i], tempContainer, i);
              const newNode = tempContainer.firstElementChild;
              container.appendChild(newNode);
            }
          }
        }
        
        // 恢复展开状态
        if (isExpanded) {
          const toggleBtn = nodeElement.querySelector('.node-toggle');
          if (toggleBtn && childrenContainer.classList.contains('hidden')) {
            toggleNode(nodeElement, toggleBtn);
          }
        }
        
        return;
      }
      
      // 对象处理
      if (typeof newData === 'object') {
        const nodeElement = findNodeByPath(elements.treeContainer, path);
        if (!nodeElement) return;
        
        const childrenContainer = nodeElement.querySelector('.node-children');
        if (!childrenContainer) return;
        
        // 保存展开状态
        const isExpanded = !childrenContainer.classList.contains('hidden');
        const key = path.join('.');
        state.nodeStates.set(key, isExpanded);
        
        // 获取新旧键（保持顺序）
        const oldKeys = Object.keys(oldData);
        const newKeys = Object.keys(newData);
        
        // 删除已移除的键
        oldKeys.forEach(key => {
          if (!newKeys.includes(key)) {
            removeNodeByPath(elements.treeContainer, [...path, key]);
          }
        });
        
        // 按新键顺序更新或添加
        newKeys.forEach(key => {
          const childPath = [...path, key];
          if (oldKeys.includes(key)) {
            updateNodesInOrder(oldData[key], newData[key], childPath);
          } else {
            const parentNode = findNodeByPath(elements.treeContainer, path);
            if (parentNode) {
              const container = parentNode.querySelector('.node-children') || parentNode;
              const siblings = Array.from(container.querySelectorAll('.node-container'));
              
              // 确定插入位置
              const newKeyIndex = newKeys.indexOf(key);
              let insertAfterIndex = -1;
              
              for (let i = newKeyIndex - 1; i >= 0; i--) {
                const prevKey = newKeys[i];
                const prevNode = siblings.find(s => s.dataset.key === prevKey);
                if (prevNode) {
                  insertAfterIndex = siblings.indexOf(prevNode);
                  break;
                }
              }
              
              // 创建并插入新节点
              const tempContainer = document.createElement('div');
              renderTreeNode(newData[key], tempContainer, key);
              const newNode = tempContainer.firstElementChild;
              
              if (insertAfterIndex === -1) {
                if (siblings.length > 0) {
                  container.insertBefore(newNode, siblings[0]);
                } else {
                  container.appendChild(newNode);
                }
              } else if (insertAfterIndex + 1 < siblings.length) {
                container.insertBefore(newNode, siblings[insertAfterIndex + 1]);
              } else {
                container.appendChild(newNode);
              }
            }
          }
        });
        
        // 恢复展开状态
        if (isExpanded) {
          const toggleBtn = nodeElement.querySelector('.node-toggle');
          if (toggleBtn && childrenContainer.classList.contains('hidden')) {
            toggleNode(nodeElement, toggleBtn);
          }
        }
        
        return;
      }
    }
    
    // 从树结构更新JSON编辑器
    function updateFromTree() {
      if (state.editing || !state.jsonData) return;
      
      try {
        state.editing = true;
        clearErrorMarkers(); // 清除错误标记
        hideErrorPanel(); // 隐藏错误面板
        state.lastError = null;
        elements.errorStatus.classList.add('hidden');
        elements.errorLocation.classList.add('hidden');
        
        // 保存光标位置和滚动位置
        const cursorPos = elements.jsonEditor.selectionStart;
        const scrollTop = elements.jsonEditor.scrollTop;
        
        // 更新编辑器
        elements.jsonEditor.value = JSON.stringify(state.jsonData, null, 2);
        updateLineNumbers();
        state.charCount = elements.jsonEditor.value.length;
        elements.charCount.textContent = `${state.charCount} 个字符`;
        
        // 恢复光标和滚动位置
        setTimeout(() => {
          try {
            const newCursorPos = Math.min(cursorPos, elements.jsonEditor.value.length);
            elements.jsonEditor.selectionStart = newCursorPos;
            elements.jsonEditor.selectionEnd = newCursorPos;
            elements.jsonEditor.scrollTop = scrollTop;
          } catch (e) {
            console.error("恢复光标位置失败:", e);
          }
        }, 0);
        
      } catch (error) {
        console.error('更新JSON编辑器失败:', error);
        showNotification('更新JSON失败', 'error');
      } finally {
        state.editing = false;
      }
    }
    
    // 按路径删除节点
    function removeNodeByPath(root, path) {
      try {
        const nodeElement = findNodeByPath(root, path);
        if (nodeElement && nodeElement.parentNode) {
          nodeElement.remove();
        }
      } catch (error) {
        console.error("删除节点失败:", error);
      }
    }
    
    // 根据路径查找节点元素
    function findNodeByPath(root, path) {
      try {
        if (!Array.isArray(path) || path.length === 0) {
          return root.querySelector('.node-container[data-key="root"]');
        }
        
        let current = root.querySelector('.node-container[data-key="root"]');
        if (!current) return null;
        
        for (const key of path) {
          if (!current) break;
          
          const childrenContainer = current.querySelector('.node-children');
          if (!childrenContainer) {
            current = null;
            break;
          }
          
          // 处理数字键和字符串键的情况
          const escapedKey = CSS.escape(key.toString());
          current = childrenContainer.querySelector(`.node-container[data-key="${escapedKey}"]`);
        }
        
        return current;
      } catch (error) {
        console.error("查找节点路径失败:", error);
        return null;
      }
    }
    
    // 计算节点数量
    function countNodes(data) {
      try {
        let count = 0;
        
        function traverse(obj) {
          count++;
          if (typeof obj === 'object' && obj !== null) {
            if (Array.isArray(obj)) {
              obj.forEach(item => traverse(item));
            } else {
              Object.values(obj).forEach(value => traverse(value));
            }
          }
        }
        
        traverse(data);
        return count;
      } catch (error) {
        console.error("计算节点数量失败:", error);
        return 0;
      }
    }
    
    // 渲染树节点
    function renderTreeNode(data, parentElement, key, isRoot = false) {
      try {
        const nodeType = getType(data);
        const isObjectOrArray = nodeType === 'object' || nodeType === 'array';
        
        // 创建节点容器
        const nodeContainer = document.createElement('div');
        nodeContainer.className = 'node-container';
        nodeContainer.dataset.key = key;
        nodeContainer.dataset.type = nodeType;
        
        // 创建节点头部
        const nodeHeader = document.createElement('div');
        nodeHeader.className = 'node-header flex items-center py-1 px-2 hover:bg-slate-100 rounded-md transition-colors cursor-pointer';
        
        // 处理键名显示
        if (!isRoot) {
          const keyElement = document.createElement('span');
          keyElement.className = 'node-key text-primary font-medium mr-2';
          keyElement.textContent = nodeType === 'array' ? `[${key}]` : `"${key}":`;
          nodeHeader.appendChild(keyElement);
        }
        
        // 添加展开/折叠按钮
        if (isObjectOrArray) {
          const toggleBtn = document.createElement('button');
          toggleBtn.className = 'node-toggle mr-1 text-slate-500 hover:text-primary transition-colors';
          toggleBtn.innerHTML = '<i class="fa fa-plus-square-o"></i>';
          toggleBtn.addEventListener('click', (e) => {
            e.stopPropagation();
            toggleNode(nodeContainer, toggleBtn);
            
            // 更新全局展开状态
            updateAllExpandedState();
          });
          nodeHeader.appendChild(toggleBtn);
        }
        
        // 添加类型和值显示
        const valueElement = document.createElement('div');
        valueElement.className = 'flex items-center flex-1';
        
        const typeBadge = document.createElement('span');
        typeBadge.className = `mr-2 px-1.5 py-0.2 rounded text-xs font-medium ${getTypeColorClass(nodeType)}`;
        typeBadge.textContent = nodeType;
        valueElement.appendChild(typeBadge);
        
        if (!isObjectOrArray) {
          // 基本类型
          const valueSpan = document.createElement('span');
          valueSpan.className = 'node-value text-slate-700';
          valueSpan.textContent = formatValue(data, nodeType);
          
          // 编辑输入框
          const valueInput = document.createElement('input');
          valueInput.type = 'text';
          valueInput.className = 'node-input hidden bg-white border border-slate-300 rounded px-2 py-0.5 text-sm focus:outline-none focus:ring-1 focus:ring-primary focus:border-primary';
          valueInput.value = formatValue(data, nodeType, true);
          
          // 点击编辑
          valueSpan.addEventListener('click', (e) => {
            try {
              e.stopPropagation();
              valueSpan.classList.add('hidden');
              valueInput.classList.remove('hidden');
              valueInput.focus();
              valueInput.dataset.originalValue = valueInput.value;
            } catch (error) {
              console.error("切换编辑模式失败:", error);
            }
          });
          
          // 保存编辑
          valueInput.addEventListener('blur', () => {
            saveEditedValue(valueInput, valueSpan, nodeContainer);
          });
          
          // 按键处理
          valueInput.addEventListener('keydown', (e) => {
            try {
              if (e.key === 'Enter') {
                saveEditedValue(valueInput, valueSpan, nodeContainer);
              } else if (e.key === 'Escape') {
                valueInput.value = valueInput.dataset.originalValue;
                valueInput.classList.add('hidden');
                valueSpan.classList.remove('hidden');
              }
            } catch (error) {
              console.error("编辑按键处理失败:", error);
            }
          });
          
          valueElement.appendChild(valueSpan);
          valueElement.appendChild(valueInput);
        } else {
          // 对象和数组
          const infoSpan = document.createElement('span');
          infoSpan.className = 'node-info text-slate-500';
          infoSpan.textContent = nodeType === 'array' ? 
            `包含 ${data ? data.length : 0} 个元素` : 
            `包含 ${data ? Object.keys(data).length : 0} 个属性`;
          valueElement.appendChild(infoSpan);
        }
        
        nodeHeader.appendChild(valueElement);
        nodeContainer.appendChild(nodeHeader);
        
        // 子节点容器
        if (isObjectOrArray && data) {
          const childrenContainer = document.createElement('div');
          childrenContainer.className = `node-children ml-6 mt-1 space-y-1 hidden`;
          nodeContainer.appendChild(childrenContainer);
          
          // 渲染子节点
          if (nodeType === 'array') {
            data.forEach((item, index) => {
              renderTreeNode(item, childrenContainer, index);
            });
          } else {
            Object.keys(data).forEach(childKey => {
              renderTreeNode(data[childKey], childrenContainer, childKey);
            });
          }
          
          // 默认展开状态 (使用全局状态)
          const path = getKeyPath(nodeContainer);
          const keyStr = path.join('.');
          if (isRoot || state.allExpanded) {
            toggleNode(nodeContainer, nodeHeader.querySelector('.node-toggle'));
          }
        }
        
        parentElement.appendChild(nodeContainer);
      } catch (error) {
        console.error("渲染树节点失败:", error);
        const errorNode = document.createElement('div');
        errorNode.className = 'text-danger p-2';
        errorNode.textContent = `渲染错误: ${error.message}`;
        parentElement.appendChild(errorNode);
      }
    }
    
    // 保存编辑后的值
    function saveEditedValue(input, span, nodeContainer) {
      try {
        const keyPath = getKeyPath(nodeContainer);
        const originalType = nodeContainer.dataset.type;
        const newValue = parseValue(input.value, originalType);
        
        // 保存状态和位置
        const key = keyPath.join('.');
        const isExpanded = nodeContainer.querySelector('.node-children')?.classList.contains('hidden') === false;
        const parentNode = nodeContainer.parentElement;
        const nextSibling = nodeContainer.nextSibling;
        
        // 更新数据
        updateValueAtPath(state.jsonData, keyPath, newValue);
        
        // 更新UI
        const nodeType = getType(newValue);
        span.textContent = formatValue(newValue, nodeType);
        input.value = formatValue(newValue, nodeType, true);
        input.classList.add('hidden');
        span.classList.remove('hidden');
        
        // 类型变化时重新渲染
        if (nodeType !== originalType) {
          if (parentNode && nodeContainer.parentNode) {
            nodeContainer.remove();
            
            // 相同位置插入新节点
            const tempContainer = document.createElement('div');
            renderTreeNode(newValue, tempContainer, nodeContainer.dataset.key);
            const newNode = tempContainer.firstElementChild;
            
            if (newNode) {
              if (nextSibling) {
                parentNode.insertBefore(newNode, nextSibling);
              } else {
                parentNode.appendChild(newNode);
              }
              
              // 恢复展开状态
              if (isExpanded || state.allExpanded) {
                const toggleBtn = newNode.querySelector('.node-toggle');
                if (toggleBtn) {
                  toggleNode(newNode, toggleBtn);
                }
              }
            }
          }
        }
        
        // 更新编辑器
        updateFromTree();
        
        showNotification('值已更新，保持原有顺序', 'success');
      } catch (error) {
        showNotification(`更新失败: ${error.message}`, 'error');
        console.error('保存值失败:', error);
      }
    }
    
    // 获取节点的键路径
    function getKeyPath(nodeElement) {
      try {
        if (!nodeElement || !nodeElement.dataset) return [];
        
        const path = [];
        let current = nodeElement;
        
        while (current && current.dataset && current.dataset.key && current.dataset.key !== 'root') {
          path.unshift(current.dataset.key);
          current = current.parentElement?.closest('.node-container');
          if (!current) break;
        }
        
        return path;
      } catch (error) {
        console.error("获取节点路径失败:", error);
        return [];
      }
    }
    
    // 根据路径更新值
    function updateValueAtPath(obj, path, value) {
      try {
        if (!obj || !Array.isArray(path) || path.length === 0) return;
        
        let current = obj;
        
        for (let i = 0; i < path.length - 1; i++) {
          const key = path[i];
          if (current[key] === undefined) {
            throw new Error(`路径不存在: ${path.slice(0, i+1).join('.')}`);
          }
          current = current[key];
        }
        
        current[path[path.length - 1]] = value;
      } catch (error) {
        console.error("更新路径值失败:", error);
        throw error;
      }
    }
    
    // 切换节点展开/折叠状态
    function toggleNode(nodeContainer, toggleBtn) {
      try {
        const childrenContainer = nodeContainer.querySelector('.node-children');
        if (!childrenContainer) return;
        
        // 保存状态
        const path = getKeyPath(nodeContainer);
        const key = path.join('.');
        
        if (childrenContainer.classList.contains('hidden')) {
          // 展开
          childrenContainer.classList.remove('hidden');
          childrenContainer.classList.add('slide-down');
          toggleBtn.innerHTML = '<i class="fa fa-minus-square-o"></i>';
          state.nodeStates.set(key, true);
        } else {
          // 折叠
          childrenContainer.classList.add('hidden');
          childrenContainer.classList.remove('slide-down');
          toggleBtn.innerHTML = '<i class="fa fa-plus-square-o"></i>';
          state.nodeStates.set(key, false);
        }
      } catch (error) {
        console.error("切换节点状态失败:", error);
      }
    }
    
    // 更新展开/折叠按钮文本
    function updateExpandCollapseButtonText() {
      const iconElement = elements.toggleExpandCollapseBtn.querySelector('i');
      const textElement = elements.toggleExpandCollapseBtn.querySelector('span');
      
      if (state.allExpanded) {
        iconElement.className = 'fa fa-minus-square-o mr-1';
        textElement.textContent = '全部折叠';
      } else {
        iconElement.className = 'fa fa-plus-square-o mr-1';
        textElement.textContent = '全部展开';
      }
    }
    
    // 检查是否所有节点都已展开
    function checkAllExpanded() {
      const toggleButtons = document.querySelectorAll('.node-toggle');
      if (toggleButtons.length === 0) return false;
      
      // 检查是否所有按钮都处于展开状态
      for (const btn of toggleButtons) {
        if (btn.innerHTML.includes('plus-square-o')) {
          return false;
        }
      }
      return true;
    }
    
    // 更新全局展开状态
    function updateAllExpandedState() {
      state.allExpanded = checkAllExpanded();
      updateExpandCollapseButtonText();
    }
    
    // 切换全部展开/折叠状态
    function toggleAllExpandCollapse() {
      if (state.allExpanded) {
        // 当前是全部展开状态，需要折叠所有
        collapseAllNodes();
        state.allExpanded = false;
      } else {
        // 当前是折叠状态，需要展开所有
        expandAllNodes();
        state.allExpanded = true;
      }
      
      updateExpandCollapseButtonText();
    }
    
    // 展开所有节点
    function expandAllNodes() {
      try {
        document.querySelectorAll('.node-toggle').forEach(btn => {
          const nodeContainer = btn.closest('.node-container');
          const childrenContainer = nodeContainer.querySelector('.node-children');
          
          if (childrenContainer && childrenContainer.classList.contains('hidden')) {
            toggleNode(nodeContainer, btn);
          }
        });
      } catch (error) {
        console.error("展开所有节点失败:", error);
        showNotification("无法展开所有节点", "error");
      }
    }
    
    // 折叠所有节点
    function collapseAllNodes() {
      try {
        document.querySelectorAll('.node-toggle:not(:first-child)').forEach(btn => {
          const nodeContainer = btn.closest('.node-container');
          const childrenContainer = nodeContainer.querySelector('.node-children');
          
          if (childrenContainer && !childrenContainer.classList.contains('hidden')) {
            toggleNode(nodeContainer, btn);
          }
        });
      } catch (error) {
        console.error("折叠所有节点失败:", error);
        showNotification("无法折叠所有节点", "error");
      }
    }
    
    // 格式化JSON
    function formatJson() {
      try {
        if (!elements.jsonEditor.value.trim()) return;
        
        // 尝试格式化前先检查是否有错误
        let parsed;
        try {
          parsed = JSON.parse(elements.jsonEditor.value);
        } catch (error) {
          const errorInfo = parseJsonError(error, elements.jsonEditor.value);
          showNotification(`格式化失败: ${errorInfo.message} (第 ${errorInfo.line} 行)`, 'error');
          markErrorLocation(errorInfo);
          prepareErrorPanel(errorInfo, elements.jsonEditor.value);
          showErrorPanel(); // 显示错误面板
          return;
        }
        
        elements.jsonEditor.value = JSON.stringify(parsed, null, 2);
        updateLineNumbers();
        updateFromJsonEditor();
        showNotification('JSON已格式化，保持原有顺序', 'success');
      } catch (error) {
        showNotification('格式化失败，JSON格式错误', 'error');
      }
    }
    
    // 压缩JSON
    function compressJson() {
      try {
        if (!elements.jsonEditor.value.trim()) return;
        
        // 尝试压缩前先检查是否有错误
        let parsed;
        try {
          parsed = JSON.parse(elements.jsonEditor.value);
        } catch (error) {
          const errorInfo = parseJsonError(error, elements.jsonEditor.value);
          showNotification(`压缩失败: ${errorInfo.message} (第 ${errorInfo.line} 行)`, 'error');
          markErrorLocation(errorInfo);
          prepareErrorPanel(errorInfo, elements.jsonEditor.value);
          showErrorPanel(); // 显示错误面板
          return;
        }
        
        elements.jsonEditor.value = JSON.stringify(parsed);
        updateLineNumbers();
        updateFromJsonEditor();
        showNotification('JSON已压缩，保持原有顺序', 'success');
      } catch (error) {
        showNotification('压缩失败，JSON格式错误', 'error');
      }
    }
    
    // 复制JSON
    function copyJson() {
      try {
        if (!elements.jsonEditor.value.trim()) {
          showNotification('没有可复制的内容', 'warning');
          return;
        }
        
        // 检查JSON是否有效
        try {
          JSON.parse(elements.jsonEditor.value);
        } catch (error) {
          if (confirm('JSON格式无效，仍要复制吗？')) {
            // 继续复制
          } else {
            return;
          }
        }
        
        elements.jsonEditor.select();
        document.execCommand('copy');
        showNotification('JSON已复制到剪贴板', 'success');
      } catch (error) {
        console.error("复制失败:", error);
        showNotification('复制失败，请手动复制', 'error');
      }
    }
    
    // 清空内容
    function clearAll() {
      try {
        if (confirm('确定要清空所有内容吗？')) {
          elements.jsonEditor.value = '';
          updateLineNumbers();
          updateFromJsonEditor();
          showNotification('内容已清空', 'info');
        }
      } catch (error) {
        console.error("清空失败:", error);
        showNotification('清空失败', 'error');
      }
    }
    
    // 显示通知
    function showNotification(message, type = 'info') {
      try {
        const notification = elements.notification;
        notification.textContent = '';
        
        // 设置样式
        notification.className = 'fixed bottom-4 right-4 px-4 py-3 rounded-md shadow-lg transform translate-y-20 opacity-0 transition-all duration-300 flex items-center space-x-2 z-50';
        
        let bgColor, textColor, icon;
        
        switch (type) {
          case 'success':
            bgColor = 'bg-secondary/90';
            textColor = 'text-white';
            icon = 'fa-check-circle';
            break;
          case 'error':
            bgColor = 'bg-danger/90';
            textColor = 'text-white';
            icon = 'fa-exclamation-circle';
            break;
          case 'warning':
            bgColor = 'bg-warning/90';
            textColor = 'text-white';
            icon = 'fa-exclamation-triangle';
            break;
          default:
            bgColor = 'bg-primary/90';
            textColor = 'text-white';
            icon = 'fa-info-circle';
        }
        
        notification.classList.add(bgColor, textColor);
        
        // 添加图标和消息
        const iconElement = document.createElement('i');
        iconElement.className = `fa ${icon}`;
        notification.appendChild(iconElement);
        
        const textElement = document.createElement('span');
        textElement.textContent = message;
        notification.appendChild(textElement);
        
        // 显示通知
        setTimeout(() => {
          notification.classList.remove('translate-y-20', 'opacity-0');
        }, 10);
        
        // 3秒后隐藏
        setTimeout(() => {
          notification.classList.add('translate-y-20', 'opacity-0');
        }, 3000);
      } catch (error) {
        console.error("显示通知失败:", error);
      }
    }
    
    // 工具函数：获取值的类型
    function getType(value) {
      if (value === null) return 'null';
      if (Array.isArray(value)) return 'array';
      return typeof value;
    }
    
    // 工具函数：根据类型获取颜色类
    function getTypeColorClass(type) {
      const classes = {
        'string': 'bg-blue-100 text-blue-800',
        'number': 'bg-green-100 text-green-800',
        'boolean': 'bg-purple-100 text-purple-800',
        'null': 'bg-gray-100 text-gray-800',
        'object': 'bg-yellow-100 text-yellow-800',
        'array': 'bg-orange-100 text-orange-800'
      };
      
      return classes[type] || 'bg-gray-100 text-gray-800';
    }
    
    // 工具函数：格式化值的显示
    function formatValue(value, type, forInput = false) {
      try {
        switch (type) {
          case 'string':
            return forInput ? value : `"${value}"`;
          case 'boolean':
            return value.toString();
          case 'null':
            return 'null';
          case 'number':
            if (Math.abs(value) > 999999999999) {
              return value.toString();
            }
            return value.toString();
          default:
            return value.toString();
        }
      } catch (error) {
        console.error("格式化值失败:", error);
        return String(value);
      }
    }
    
    // 工具函数：解析输入的值为正确的类型
    function parseValue(input, originalType) {
      try {
        if (originalType === 'string') {
          return input;
        }
        
        if (originalType === 'number') {
          const num = parseFloat(input);
          if (isNaN(num)) throw new Error('不是有效的数字');
          return num;
        }
        
        if (originalType === 'boolean') {
          if (input.toLowerCase() === 'true') return true;
          if (input.toLowerCase() === 'false') return false;
          throw new Error('必须是 true 或 false');
        }
        
        if (originalType === 'null') {
          if (input.toLowerCase() === 'null') return null;
          throw new Error('必须是 null');
        }
        
        return input;
      } catch (error) {
        console.error("解析值失败:", error);
        throw error;
      }
    }
    
    // 检查数据结构是否发生显著变化
    function isStructureChanged(oldData, newData) {
      try {
        if (getType(oldData) !== getType(newData)) return true;
        
        if (oldData === null || newData === null) return oldData !== newData;
        
        if (Array.isArray(oldData) && Array.isArray(newData)) {
          const lengthDiff = Math.abs(oldData.length - newData.length);
          if (lengthDiff > 10 || lengthDiff / Math.max(oldData.length, 1) > 0.5) {
            return true;
          }
        }
        
        if (typeof oldData === 'object' && typeof newData === 'object' &&
            !Array.isArray(oldData) && !Array.isArray(newData)) {
          const oldKeys = Object.keys(oldData).length;
          const newKeys = Object.keys(newData).length;
          const keyDiff = Math.abs(oldKeys - newKeys);
          if (keyDiff > 5 || keyDiff / Math.max(oldKeys, 1) > 0.5) {
            return true;
          }
        }
        
        return false;
      } catch (error) {
        console.error("检查结构变化失败:", error);
        return true;
      }
    }
    
    // 初始化调整大小功能
    function initResizing() {
      // 水平调整（桌面）
      elements.horizontalResizer.addEventListener('mousedown', (e) => {
        state.isResizing = true;
        document.body.style.cursor = 'col-resize';
        document.body.style.userSelect = 'none';
        
        const handleMouseMove = (e) => {
          if (!state.isResizing) return;
          
          const containerRect = elements.toolContainer.getBoundingClientRect();
          const containerWidth = containerRect.width;
          const relativeX = e.clientX - containerRect.left;
          
          // 限制最小宽度为20%
          state.panelRatio = Math.max(0.2, Math.min(0.8, relativeX / containerWidth));
          
          // 应用新比例
          elements.panels[0].style.width = `${state.panelRatio * 100}%`;
          elements.panels[1].style.width = `${(1 - state.panelRatio) * 100}%`;
        };
        
        const handleMouseUp = () => {
          state.isResizing = false;
          document.body.style.cursor = '';
          document.body.style.userSelect = '';
          
          document.removeEventListener('mousemove', handleMouseMove);
          document.removeEventListener('mouseup', handleMouseUp);
        };
        
        document.addEventListener('mousemove', handleMouseMove);
        document.addEventListener('mouseup', handleMouseUp);
        
        e.preventDefault();
      });
      
      // 垂直调整（移动设备）
      elements.verticalResizer.addEventListener('touchstart', (e) => {
        state.isResizing = true;
        document.body.style.touchAction = 'none';
        
        const handleTouchMove = (e) => {
          if (!state.isResizing) return;
          
          const containerRect = elements.toolContainer.getBoundingClientRect();
          const containerHeight = containerRect.height;
          const touchY = e.touches[0].clientY - containerRect.top;
          
          // 限制最小高度为20%
          state.verticalPanelRatio = Math.max(0.2, Math.min(0.8, touchY / containerHeight));
          
          // 应用新比例
          elements.panels[0].style.height = `${state.verticalPanelRatio * 100}%`;
          elements.panels[1].style.height = `${(1 - state.verticalPanelRatio) * 100}%`;
          
          e.preventDefault();
        };
        
        const handleTouchEnd = () => {
          state.isResizing = false;
          document.body.style.touchAction = '';
          
          document.removeEventListener('touchmove', handleTouchMove);
          document.removeEventListener('touchend', handleTouchEnd);
        };
        
        document.addEventListener('touchmove', handleTouchMove);
        document.addEventListener('touchend', handleTouchEnd);
        
        e.preventDefault();
      });
      
      // 窗口大小变化时重新计算
      window.addEventListener('resize', () => {
        if (window.innerWidth >= 768) {
          // 桌面视图
          elements.panels[0].style.width = `${state.panelRatio * 100}%`;
          elements.panels[1].style.width = `${(1 - state.panelRatio) * 100}%`;
        } else {
          // 移动视图
          elements.panels[0].style.height = `${state.verticalPanelRatio * 100}%`;
          elements.panels[1].style.height = `${(1 - state.verticalPanelRatio) * 100}%`;
        }
      });
    }
    
    // 绑定事件监听
    function bindEvents() {
      try {
        // JSON编辑器事件
        let debounceTimer;
        elements.jsonEditor.addEventListener('input', () => {
          updateLineNumbers();
          clearTimeout(debounceTimer);
          debounceTimer = setTimeout(() => {
            updateFromJsonEditor();
          }, 300);
        });
        
        elements.jsonEditor.addEventListener('scroll', () => {
          elements.lineNumbers.scrollTop = elements.jsonEditor.scrollTop;
        });
        
        // 错误面板事件
        elements.closeErrorBtn.addEventListener('click', toggleErrorPanel);
        
        // 展开/折叠切换按钮事件
        elements.toggleExpandCollapseBtn.addEventListener('click', toggleAllExpandCollapse);
        
        // 其他按钮事件
        elements.formatBtn.addEventListener('click', formatJson);
        elements.compressBtn.addEventListener('click', compressJson);
        elements.copyBtn.addEventListener('click', copyJson);
        elements.clearBtn.addEventListener('click', clearAll);
        
        // 初始化调整大小功能
        initResizing();
        
      } catch (error) {
        console.error("绑定事件失败:", error);
      }
    }
    
    // 初始化应用
    function init() {
      try {
        bindEvents();
        initSampleData();
        updateLineNumbers();
      } catch (error) {
        console.error("初始化应用失败:", error);
        showNotification("应用初始化失败", "error");
      }
    }
    
    // 启动应用
    document.addEventListener('DOMContentLoaded', init);
  </script>
</body>
</html>
